
        
package com.mall.order.service;


import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ctg.mse.common.exception.BizException;
import com.ctg.mse.common.model.error.CommonCode;
import com.ctg.mse.common.model.result.*;
import com.ctg.mse.common.utils.EncryptUtil;
import com.mall.order.common.DemoAppBaseInfoConverter;
import com.mall.order.dao.entity.DemoAppBaseInfo;
import com.mall.order.dao.mapper.DemoAppBaseInfoMapper;
import com.mall.order.error.DemoAppError;
import com.mall.order.model.DemoAppBaseInfoVo;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.ctg.mse.common.cache.RedisSingle;
import com.alicp.jetcache.anno.*;

/**
 * @Description: MySQL + mybatis plus 基本用法，实现基本的crud + 分页查询
 * @Author: zhouzhao
 * @CreateTime: 2022/10/19 10:38:29
 */
@Service
public class MysqlDemoService {

    private static final Logger LOGGER = LoggerFactory.getLogger(MysqlDemoService.class);

    @Autowired
    private DemoAppBaseInfoMapper demoAppBaseInfoMapper;

        @Autowired
    private RedisSingle redisClient;
    

        /**
     * 缓存key 前缀
     */
    private static final String APP_INFO_KEY_PREFIX = "AppBaseInfo";
    

    /**
     * Transactional 这个事务通过代理实现，rollbackFor = Exception.class 是任何异常都回滚
     * 注意：如果本类调用，需要注入自己才能生效
     * 例如:
     *
     * @param vo
     * @return
     * @Autowired private MySQLDemoService mySQLDemoService;
     * <p>
     * mySQLDemoService.addAppInfo(vo)
     */
        @CacheUpdate(name = APP_INFO_KEY_PREFIX, key = "#vo.appId", value = "#vo")
        @Transactional(rollbackFor = Exception.class)
    public DemoAppBaseInfoVo addAppInfo(DemoAppBaseInfoVo vo) {
        DemoAppBaseInfo demoAppBaseInfo = DemoAppBaseInfoConverter.voToDo(vo);

        demoAppBaseInfoMapper.insert(demoAppBaseInfo);
        return vo;
    }

    /**
     * @param appId
     * @return
     */
        @CacheInvalidate(name = APP_INFO_KEY_PREFIX, key = "#appId")//失效缓存
        public Boolean deleteAppInfo(String appId) {
        int line = demoAppBaseInfoMapper.deleteById(appId);
        return line == 1;
    }

        /**
     * jetcache文档可以看
     * 注解使用说明 https://github.com/alibaba/jetcache/wiki/MethodCache_CN
     * 如果使用二级缓存，且对缓存一致性要求高，可以参考增强 https://blog.csdn.net/qq_27384769/article/details/112776462
     * 可以参考 {#com.ctg.mse.common.cache.jetcache.JetCacheConfig} 注释
     * <p>
     * 如果同一类调用本地方法，cache注解会不生效。需要在bean中通过@Autowired注入它自己，然后在用注入的实例代替this来调用。
     * https://github.com/alibaba/jetcache/wiki/FAQ_CN
     * name 用来统计的,name属性还会用做cache的key前缀
     * key是设置在redis的
     * 这里序列化到redis是用的二进制的,python等用不了了
     *
     * @param appId
     * @return
     */
    @Cached(name = APP_INFO_KEY_PREFIX, key = "#appId", expire = 600, cacheType = CacheType.BOTH, localLimit = 500)
    //100s 没有访问后，每300s 后刷新，
    @CacheRefresh(refresh = 300, stopRefreshAfterLastAccess = 100, refreshLockTimeout = 30)
        public DemoAppBaseInfoVo getAppByAppId(String appId) {

        DemoAppBaseInfo DO = demoAppBaseInfoMapper.selectById(appId);
        if (DO == null) {
            throw new BizException(CommonCode.DATA_NOT_EXIST);
        }
        DemoAppBaseInfoVo VO = DemoAppBaseInfoConverter.doToVo(DO);

                //直接使用redis client
        long start = System.currentTimeMillis();
        redisClient.set(appId, DO, 100L);
        LOGGER.info("getAppByAppId:" + appId + " set redis cluster use:" + (System.currentTimeMillis() - start) + "ms");
        DemoAppBaseInfo redisDo = redisClient.get(appId, DemoAppBaseInfo.class);
        
        return VO;
    }


    public ResultPageModel<DemoAppBaseInfoVo> getApiAppInfoPage(String name, Integer pageSize, Integer pageNum) {
        QueryWrapper<DemoAppBaseInfo> query = new QueryWrapper<>();
        if (StringUtils.isNotBlank(name)) {
            query.eq("name", name);
        }
        query.orderByDesc("modify_time");

        IPage<DemoAppBaseInfo> pg = new Page<DemoAppBaseInfo>(pageNum, pageSize);
        pg = demoAppBaseInfoMapper.selectPage(pg, query);

        PageInfo<DemoAppBaseInfoVo> page = new PageInfo(pg.getRecords(), pg.getTotal(), pageSize, pageNum);
        return ResultPageModel.newSuccess(page);
    }

        //关于缓存注解使用，如果vo的属性就是要设置的全部属性，可以使用 CacheUpdate 注解，否则需要手工处理
    //@CacheUpdate(name = APP_INFO_KEY_PREFIX, key = "#vo.appCode", value = "#vo")
            public DemoAppBaseInfoVo modifyAppInfo(DemoAppBaseInfoVo vo) {
        DemoAppBaseInfo demoAppBaseInfo = DemoAppBaseInfoConverter.voToDo(vo);
        demoAppBaseInfo.setName(null);

        QueryWrapper<DemoAppBaseInfo> query = new QueryWrapper<>();
        query.eq("name", vo.getName());
        query.orderByAsc("app_id");

        int line = demoAppBaseInfoMapper.update(demoAppBaseInfo, query);
        if (line == 0) {
            //不存在就抛出异常,这样就不会put进缓存
            throw new BizException(CommonCode.DATA_NOT_EXIST);
        } else {
            demoAppBaseInfo = demoAppBaseInfoMapper.selectOne(query);
            //为了缓存一致性，需要查出来返回
            vo = DemoAppBaseInfoConverter.doToVo(demoAppBaseInfo);
            return vo;
        }
    }

}
